package eth

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"math/big"
	"net/http"

	"encoding/base64"
	"strconv"
	"strings"

	"git.weinongtang.com/iuhjui/coin-kit/eth"
	"gitee.com/coin-kit/eth-tx"
	"gitee.com/coin-kit/go-ethscan"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/rlp"
)

//var baseUrl = "https://api-kovan.etherscan.io"
var baseUrl string

func init() {
	baseUrl = "http://api.etherscan.io"
}

const TokenABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"saleStartTime\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tokenSaleContract\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"emergencyERC20Drain\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"saleEndTime\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"tokenTotalAmount\",\"type\":\"uint256\"},{\"name\":\"startTime\",\"type\":\"uint256\"},{\"name\":\"endTime\",\"type\":\"uint256\"},{\"name\":\"admin\",\"type\":\"address\"}],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_burner\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"}]"

type EtherscanJson struct {
	Status  string `json:"status"`
	Message string `json:"message"`
	Result  string `json:"result"`
}

type ErrorJson struct {
	Message string `json:"message"`
}

type EtherscanJson2 struct {
	Jsonrpc string    `json:"jsonrpc"`
	Id      int       `json:"id"`
	Result  string    `json:"result"`
	Error   ErrorJson `json:"error"`
}

func SetBaseurl(url string) {
	baseUrl = url
}

func MyHttpGet(url string) (*http.Response, error) {
	return http.Get(url)
}

//获取ETH地址余额
func GetETHBalance(address string) (float64, error) {
	var jsondata EtherscanJson
	url := baseUrl + "/api?module=account&action=balance&tag=latest&address=" + address
	resp, err := MyHttpGet(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	input, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	json.Unmarshal([]byte(input), &jsondata)

	if jsondata.Message != "OK" {
		return 0, errors.New("报错Message != OK:" + jsondata.Message)
	}
	str := jsondata.Result
	var cou = new(big.Float)
	cou.SetString(str)
	cou = cou.Quo(cou, big.NewFloat(1000000000000000000.0))
	v2, _ := strconv.ParseFloat(cou.String(), 64)

	return v2, nil
}

//获取ETH地址token余额
func GetETHTokenBalance(address, contractaddress string) (float64, error) {
	var jsondata EtherscanJson
	url := baseUrl + "/api?module=account&action=tokenbalance&tag=latest&address=" + address + "&contractaddress=" + contractaddress
	resp, err := MyHttpGet(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	input, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	json.Unmarshal([]byte(input), &jsondata)

	if jsondata.Message != "OK" {
		return 0, errors.New("报错Message != OK:" + jsondata.Message)
	}
	str := jsondata.Result
	var cou = new(big.Float)
	cou.SetString(str)
	cou = cou.Quo(cou, big.NewFloat(1000000000000000000.0))
	v2, _ := strconv.ParseFloat(cou.String(), 64)

	return v2, nil

}

//获取eth地址Nonce
func GetETHNonce(address string) (int64, error) {
	var jsondata EtherscanJson2
	url := baseUrl + "/api?module=proxy&action=eth_getTransactionCount&tag=latest&address=" + address
	resp, err := MyHttpGet(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()
	input, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	json.Unmarshal([]byte(input), &jsondata)
	if len(jsondata.Result) <= 2 {
		return 0, errors.New("数据出错," + jsondata.Result)
	}
	v2, _ := strconv.ParseInt(jsondata.Result[2:], 16, 64)
	return v2, nil
}

//ETH广播交易
func SendEthTransaction(signed string) (string, error) {
	var jsondata EtherscanJson2
	url := baseUrl + "/api?module=proxy&action=eth_sendRawTransaction&hex=" + signed

	resp, err := MyHttpGet(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	input, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	json.Unmarshal([]byte(input), &jsondata)
	if len(jsondata.Result) <= 0 {
		return "", errors.New(jsondata.Error.Message)
	}

	return jsondata.Result, nil
}

//获取ETH待签名串
func GetEthUnSigneString(nonce uint64, from, to string, amount, fee float64) (string, error) {
	totleamount, err := GetETHBalance(from)
	if err != nil {
		return "", errors.New("获取发币地址余额报错" + err.Error())
	}

	if totleamount < amount {
		return "", errors.New("发币地址余额不足")
	}
	amount1 := big.NewFloat(amount)
	tenDecimal1 := big.NewFloat(math.Pow(10, float64(18)))
	convertAmount, _ := new(big.Float).Mul(tenDecimal1, amount1).Int(&big.Int{})

	Gas_Price := big.NewFloat(fee / 21000.0)
	tenDecimal := big.NewFloat(math.Pow(10, float64(18)))
	Gas_Price_1, _ := new(big.Float).Mul(tenDecimal, Gas_Price).Int(&big.Int{})

	balance1 := big.NewFloat(totleamount)
	balance2, _ := new(big.Float).Mul(tenDecimal, balance1).Int(&big.Int{})

	// 生成交易
	builder := ethtx.NewBuilder()
	builder.SetFrom(from)
	builder.SetTo(to)
	builder.SetNonce(nonce)
	builder.SetPrice(Gas_Price_1)
	builder.SetBalance(balance2)
	builder.SetAmount(convertAmount)
	raw_tx, err := builder.Build()
	if err != nil {
		return "", err
	}
	// 编码到base64并附加类型头
	buf := bytes.NewBuffer(nil)
	buf.WriteByte(byte(0x03))
	buf.Write(raw_tx)
	b64_tx := base64.RawStdEncoding.EncodeToString(buf.Bytes())
	return b64_tx, nil
}

func init() {
	GetEthTokenUnSigneString(12, "0x8a60bedf4913a5358a6423ab4aad58112e6b6f15", "0x227634383e022740f2f48ddefc0ae49d48e8ed63", "0x30d049488ab263b5f6caf4bb1c6333166f01b289", "UWN", 10.0, 0.001, 0)
}

//获取token待签名串
func GetEthTokenUnSigneString(nonce uint64, from, to, contract, symbol string, amount, fee, gas_limit1 float64) (string, error) {
	totleamount, err := GetETHTokenBalance(from, contract)

	if err != nil {
		return "", errors.New("获取发币地址余额报错" + err.Error())
	}

	if totleamount < amount {
		return "", errors.New("发币地址余额不足")
	}

	amount1 := big.NewFloat(amount)
	tenDecimal11 := big.NewFloat(math.Pow(10, float64(10)))
	convertAmount1, _ := new(big.Float).Mul(tenDecimal11, amount1).Int(&big.Int{})
	a := big.NewInt(100000000)
	convertAmount := new(big.Int).Mul(convertAmount1, a)

	// 获得代价
	abi_input := ethtx.GenTrasferABI(to, convertAmount)
	EthScan, _ := ethscan.New("https://api.etherscan.io/api")
	gas_limit, err := EthScan.EthEstimateGas(from, contract, abi_input)

	fmt.Println(abi_input, gas_limit, err)
	if err != nil {
		return "", err
	}
	// 手续费处理

	limit := big.NewInt(0).SetUint64(gas_limit)
	Fee, _ := new(big.Float).Mul(big.NewFloat(math.Pow(10, float64(18))), big.NewFloat(fee)).Int(&big.Int{})
	price := big.NewInt(0).Div(Fee, limit)

	builder := ethtx.NewTokenBuilder()
	builder.SetFrom(from)
	builder.SetTo(to)
	builder.SetContract(contract)
	builder.SetNonce(nonce)
	builder.SetAmount(convertAmount)
	builder.SetPrice(price)
	builder.SetLimit(gas_limit)
	builder.Build()
	raw_tx, err := builder.Build()

	if err != nil {
		return "", err
	}
	// 编码到base64并附加类型头
	buf := bytes.NewBuffer(nil)
	buf.WriteByte(byte(0x04))
	buf.WriteByte(byte(len([]byte(symbol))))
	buf.Write([]byte(symbol))
	buf.Write(raw_tx)
	b64_tx := base64.RawStdEncoding.EncodeToString(buf.Bytes())
	return b64_tx, nil

}

type JsonHashStatusStatus struct {
	Status string `json:"status"`
}
type JsonHashStatus struct {
	Status  string               `json:"status"`
	Message string               `json:"message"`
	Result  JsonHashStatusStatus `json:"result"`
}

//检查hash确认数
func GetETHCheckHash(hash string) int {
	var jsondata JsonHashStatus
	var url = baseUrl + "/api?module=transaction&action=gettxreceiptstatus&txhash=" + hash

	resp, err := MyHttpGet(url)
	if err != nil {
		return 0
	}
	defer resp.Body.Close()
	input, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0
	}
	json.Unmarshal([]byte(input), &jsondata)
	if jsondata.Result.Status == "" {
		return 0
	} else if jsondata.Status == "1" {
		return 1
	} else {
		return -1
	}

}

//解码ETH交易
func GetETHSignedNonce(signed string) uint64 {
	st := &eth.SignedTx{}
	st.DecodeFromString(signed)

	monce := st.Tx.Nonce()
	return monce
}

func DCallWithParam(msg string) {
	fmt.Println("参数内容为:", msg)
}

type UnsignedTx struct {
	Tx *types.Transaction
}

func GetTransacData(amount *big.Int, to string) []byte {
	parsed, _ := abi.JSON(strings.NewReader(TokenABI))
	input, _ := parsed.Pack("transfer", common.HexToAddress(to), amount)
	return input
}

func NewUnsignedTx(nonce uint64, to string, amount *big.Int, data []byte) *UnsignedTx {
	ut := &UnsignedTx{}

	ut.Tx = types.NewTransaction(
		nonce,
		common.HexToAddress(to),
		amount,
		60000,
		big.NewInt(0).SetUint64(4000000000),
		data,
	)
	return ut
}

func EncodeToString(ut *UnsignedTx) (string, error) {
	buf := new(bytes.Buffer)
	err := Encode(buf, ut)
	if err != nil {
		return "", err
	}
	return "0x" + hex.EncodeToString(buf.Bytes()), nil
}

func Encode(w io.Writer, ut *UnsignedTx) error {
	err := rlp.Encode(w, ut.Tx)
	if err != nil {
		return err
	}
	return nil
}

func Decode(ut *UnsignedTx, r io.Reader) (*UnsignedTx, error) {

	ut.Tx = &types.Transaction{}
	err := rlp.Decode(r, ut.Tx)
	if err != nil {
		return ut, err
	}
	return ut, nil
}

func DecodeFromString(str string) (*UnsignedTx, error) {
	var ut *UnsignedTx
	if len(str) <= 2 {
		return ut, errors.New("unsigned tx format error")
	}
	if str[:2] == "0x" {
		str = str[2:]
	}
	raw, err := hex.DecodeString(str)
	if err != nil {
		return ut, err
	}
	buf := bytes.NewBuffer(raw)
	ut, err = Decode(ut, buf)
	if err != nil {
		return ut, err
	}
	return ut, nil
}
